library oss_upload;

import 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'package:flutter_image_compress/flutter_image_compress.dart';
import 'package:http_parser/http_parser.dart';
import 'package:intl/intl.dart';

import 'package:crypto/crypto.dart';
import 'package:dio/dio.dart';
import 'package:mime/mime.dart';
import 'package:oss_upload/utils/oss_utils.dart';
import 'package:path_provider/path_provider.dart';
import 'package:uuid/uuid.dart';

class OSSUpload {
  static const String _BASE_URL_DEBUG = 'https://studycloud.ieducc.com/base/';
  static const String _BASE_URL = "https://sc.ieducc.com/base/";
  ///阿里云OSS地址
  static const String _OSS_URL = 'https://studycloud-test.oss-cn-hangzhou.aliyuncs.com/';
  ///
  /// [filePath] 上传的文件地址
  /// [uid] 用户uid
  /// [orgId] 机构id
  /// [privateKey] 验签key
  /// [isDebug] 正式服/测试服
  /// [suffix] 文件扩展名 不传则使用文件地址结尾的扩展名
  /// [isCompress] 是否压缩(只针对图片,jpg和png)
  /// [onSendProgress] 上传进度
  /// return {url: https://studycloud-test.oss-cn-hangzhou.aliyuncs.com/9eb28780-45ff-11ec-b190-8f52abe3a3f7.jpg, size: 208418}
  /// url:oss文件地址 size: 文件大小
  static Future<Map> uploadFile(
    String filePath,
      String uid,
      String orgId,
      String privateKey,
      {
        bool isDebug = false,
        String? suffix,
    bool isCompress = true,
    ProgressCallback? onSendProgress,
    CancelToken? cancelToken,
  }) async {
    var responseToken = await _getStsToken(isDebug, uid, orgId, privateKey);
    var data = responseToken['data'];
    int lastIndex = filePath.lastIndexOf("/");
    String fileName = filePath.substring(lastIndex + 1);
    int lastpoint = filePath.lastIndexOf(".");
    suffix = suffix ?? filePath.substring(lastpoint);
    String newFileName = const Uuid().v1() + (suffix);
    File file;
    if (isCompress && (suffix.toLowerCase() == ".jpeg" || suffix.toLowerCase() == ".jpg")) {
      try {
        file = await compressImage(filePath);
      } catch (e) {
        file = File(filePath);
      }
    } else if (isCompress && suffix.toLowerCase() == ".png") {
      file = await compressImage(filePath, format: CompressFormat.png);
    } else {
      file = File(filePath);
    }
    MediaType contentType = MediaType.parse(lookupMimeType(newFileName)!);
    var multipartFile = await MultipartFile.fromFile(file.path.substring(1, file.path.length),
        filename: fileName, contentType: contentType);
    var params = {
      'Filename': newFileName, //文件名，随意
      'key': newFileName, //"可以填写文件夹名（对应于oss服务中的文件夹）/" + fileName
      'policy': OssUtil.policy,
      'OSSAccessKeyId': data["accessKeyId"], //Bucket 拥有者的AccessKeyId。
      'success_action_status': '200', //让服务端返回200，不然，默认会返回204
      'Signature': OssUtil.instance.getSignature(data["accessKeySecret"]),
      'x-oss-security-token': data["securityToken"], //临时用户授权时必须，需要携带后台返回的security-token
      'file': multipartFile //必须放在参数最后
    };
    FormData formData = FormData.fromMap(params);
    Dio dio = Dio(
      BaseOptions(
        receiveTimeout: 30,
        sendTimeout: 30,
        responseType: ResponseType.plain,
        contentType: "multipart/form-data",
      ),
    );
    await dio
        .post(_OSS_URL,
        data: formData, onSendProgress: onSendProgress, cancelToken: cancelToken)
        .catchError((error) {
      throw error;
    });
    return {
      "url": _OSS_URL + newFileName,
      "size": multipartFile.length,
    };
  }

  static Future<Map> _getStsToken(bool isDebug, String uid, String orgId, String privateKey,) async{
    Dio dio = Dio();
    Map<String, dynamic> params = {
      "nonce":Random().nextInt(999999).toString(), //随机数字
      "orgId":orgId,
      "timestamp":DateFormat("yyyy-MM-dd HH:mm:ss").format(DateTime.now()),
      "uid":uid,
    };
    params['sign'] = getSign(privateKey, params);
    String url = "${isDebug? _BASE_URL_DEBUG : _BASE_URL}oss/v1/getStsToken";
    print(url);
    var response = await dio.get(url, queryParameters: params).then((response) {
      if (response.statusCode != HttpStatus.ok && response.statusCode != HttpStatus.created) {
        throw DioError(requestOptions: response.requestOptions);
      }
      return response.data["businessData"];
    }).catchError((error){
      throw error;
    });
    return response;
  }

  ///图片压缩 quality:50 一张5M的图片大概压到200~300KB
  static Future compressImage(String path,
      {int quality = 50, CompressFormat format = CompressFormat.jpeg}) async {
    String suffix = path.substring(path.lastIndexOf(".") + 1);

    final Directory _directory = await getTemporaryDirectory();
    String cachePath = "${_directory.absolute.path}/image/";
    Directory imageDir = Directory(cachePath);
    if (!imageDir.existsSync()) {
      imageDir.createSync();
    }
    File? file = await FlutterImageCompress.compressAndGetFile(
        path, "${cachePath}/${Uuid().v1()}.$suffix",
        quality: quality, format: format);
    return file;
  }


  static String getSign(String privateKey, Map parameter) {
    String priKey = privateKey;
//    debugPrint("拼接key=value&");
    /// 拼接key=value&
    List<String> allKeys = [];
    parameter.forEach((key, value) {
      allKeys.add("$key=$value&");
    });
//    debugPrint(allKeys.toString());
//    debugPrint("排序");
    /// key排序
    allKeys.sort((obj1, obj2) {
      return obj1.compareTo(obj2);
    });
//    debugPrint(allKeys.toString());
    /// 数组转string
    String pairsString = allKeys.join("");
//    debugPrint("拼接字符串");
    /// 拼接 key
    String signString = "${pairsString}priKey=$priKey";
//    debugPrint(signString);
//    debugPrint("生成md5");
    String sign = generateMd5(signString);
//    debugPrint(sign);
    return sign;
  }

  /// md5加密
  static String generateMd5(String data) {
    var content = const Utf8Encoder().convert(data);
    var digest = md5.convert(content);
    // 这里其实就是 digest.toString()
    return digest.toString();
  }
}
